#include "can.h"

static void* pthread_can_handle(void *context)
{
	int32_t ret = 0;
    struct can* pcan;

    pcan = (struct can*)context;
    if(!pcan){
        printf("create CAN thread error\n");
        return NULL;
    }
    prctl(PR_SET_NAME, "can");

	/* set filter for only receiving packet */
	pcan->rfilter[0].can_mask = CAN_SFF_MASK;
	ret = setsockopt(pcan->socket, SOL_CAN_RAW, CAN_RAW_FILTER, &pcan->rfilter, sizeof(pcan->rfilter));
	if(ret < 0) {
		perror("set receiving filter error\n");
		return NULL;
	}
	/* reading... */
	while(pcan->running_flag){
		ret = read(pcan->socket, &pcan->frame, sizeof(pcan->frame));
		if(ret > 0) {
			printf("%s ID=%#x data length=%d\n", pcan->ifr.ifr_name, pcan->frame.can_id, pcan->frame.can_dlc);
			for (int i = 0; i < pcan->frame.can_dlc; i++)
				printf("%#x ", pcan->frame.data[i]);
			printf("\n");
		}
	}

	return NULL;
}

static int32_t can_write(struct can* pcan, uint8_t* data, uint32_t len)
{
    int32_t ret = 0;

    if(!pcan->opened){
        return -1;
    }
    /* configure can data length */
	pcan->frame.can_dlc = 8;
	printf("%s ID=%#x data length=%d\n", pcan->ifr.ifr_name, pcan->frame.can_id, pcan->frame.can_dlc);

    /* Sending data */
    ret = write(pcan->socket, &pcan->frame, sizeof(pcan->frame));  
    if (ret != len){
        DBG(DBG_ERR, "CAN send message error\n");
        return -EFAULT;
    }

    return 0;
}

static int32_t can_open(struct can* pcan)
{
	int32_t ret = 0;

	/* create socket */
	if ((pcan->socket = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
	    perror("Create socket failed");
	    return -1;
	}

	/* set up can interface */
	strcpy(pcan->ifr.ifr_name, pcan->dev);
	printf("can port is %s\n", pcan->ifr.ifr_name);
	/* assign can device */
	ioctl(pcan->socket, SIOCGIFINDEX, &pcan->ifr);
    pcan->addr.can_family = AF_CAN;
	pcan->addr.can_ifindex = pcan->ifr.ifr_ifindex;
    /* bind can device */
	ret = bind(pcan->socket, (struct sockaddr *)&pcan->addr, sizeof(pcan->addr));
	if(ret < 0) {
		perror("Bind can device failed\n");
        close(pcan->socket);
		return ret;
	}

	/* set can id 0x1F */
	pcan->frame.can_id = 0x1f;
	pcan->rfilter[0].can_id = 0x1F;

    pcan->running_flag = 1;
    ret = pthread_create(&pcan->pid, NULL, pthread_can_handle, pcan);
    if(ret < 0){
        DBG(DBG_ERR,"pthread_create error\n");
        close(pcan->socket);
        return ret;
    }
    pcan->opened = 1;

	return ret;
}

static int32_t can_close(struct can* pcan)
{
    if(!pcan->opened){
        return 0;
    }

    close(pcan->socket);
    pcan->opened = 0;
    
	return 0;
}

static int32_t can_init(struct can* pcan)
{
    int32_t ret = 0;

    INIT_LIST_HEAD(&(pcan->head));
    lock_init(&(pcan->lock));
    lock_init(&(pcan->notify_lock));
    wait_init(&(pcan->wait));

    /*
    create all sources
    ......
    */
    
    return ret;
}

static int32_t can_release(struct can* pcan)
{
    /*
    free all sources
    ......
    */

    lock_destroy((&pcan->notify_lock));
    lock_destroy((&pcan->lock));
    wait_destroy((&pcan->wait));

    return 0;
}

static int32_t can_register_notify(struct can* pcan, int32_t event, can_event_notify notify, void* object)
{
    struct can_event_action* paction;
    if(!notify || (event <= CAN_EVENT_NONE) || (event >= CAN_EVENT_MAX)){
        return -EINVAL;
    }
    paction = (struct can_event_action*)malloc(sizeof(struct can_event_action));
    if(!paction){
        DBG(DBG_ERR,"malloc error\n");
        return -ENOMEM;
    }
    paction->notify = notify;
    paction->object = object;
    lock(&(pcan->notify_lock));
    paction->next = pcan->paction[event];
    pcan->paction[event] = paction;
    unlock(&(pcan->notify_lock));
    return 0;
}

static int32_t can_unregister_notify(struct can* pcan, int32_t event, void* object)
{
    struct can_event_action *paction,* ptmp;
    if((event <= CAN_EVENT_NONE) || (event >= CAN_EVENT_MAX)){
        return -EINVAL;
    }
    lock(&(pcan->notify_lock));
    paction = pcan->paction[event];
    if(paction){
        if(paction->object == object){
            pcan->paction[event] = paction->next;
            free(paction);
        }else{
            while(paction->next){
                if(paction->next->object == object){
                    ptmp = paction->next;
                    paction->next = ptmp->next;
                    free(ptmp);
                    break;
                }
                paction = paction->next;
            }
        }
    }
    unlock(&(pcan->notify_lock));

    return 0;
}

static int32_t can_trigger_notify(struct can* pcan, int32_t event, void* context)
{
    struct can_event_action* paction;
    if((event <= CAN_EVENT_NONE) || (event >= CAN_EVENT_MAX)){
        return -EINVAL;
    }
    lock(&(pcan->notify_lock));
    paction = pcan->paction[event];
    while(paction){
        paction->notify(pcan, event, paction->object, context);
        paction = paction->next;
    }
    unlock(&(pcan->notify_lock));

    return 0;
}

static struct can_operation can_op =
{
    .init = can_init,
    .release = can_release,

    .open = can_open,
    .close = can_close,

    .write = can_write,

    .register_notify = can_register_notify,
    .unregister_notify = can_unregister_notify,
    .trigger_notify = can_trigger_notify,
};

int32_t create_init_can(struct can** pcan)
{
    int32_t ret;
    struct can* ptmp;
    (*pcan) = (struct can*)malloc(sizeof(struct can));
    if(!(*pcan)){
        DBG(DBG_ERR,"malloc error\n");
        return -ENOMEM;
    }
    ptmp = *pcan;
    memset(ptmp, 0, sizeof(struct can));
    ptmp->op = &can_op;
    ret = ptmp->op->init(ptmp);
    if(ret < 0){
        DBG(DBG_ERR,"init error\n");
        release_destroy_can(ptmp);
        return ret;
    }
    return 0;
}

void release_destroy_can(struct can* pcan)
{
    if(pcan){
        pcan->op->release(pcan);
        free(pcan);
    }
}
        